package xyz.fefine;

import java.io.IOException;
import java.io.LineNumberReader;
import java.io.Reader;
import java.util.ArrayList;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

/**
 * Created by feng_ on 2016/8/31.
 * 词法分析器
 * 由于
 */
public class Lexer {

    //没有换行
    private static String regexPat_no = "\\s*((//.*)|([0-9]+)|(\"(\\\\\"|\\\\\\\\|[^\"])*\")" +
            "|[A-Z_a-z][A-Z_a-z0-9]*|==|<=|>=|&&|\\|\\||\\p{Punct})?";

//    private static String regexPat = "\\s*((//.*)|([0-9]+)|(\"(\\\\\"|\\\\\\\\|\\\\n|[^\"])*\")" +
//            "|[A-Z_a-z][A-Z_a-z0-9]*|==|<=|>=|&&|\\|\\||\\p{Punct})?";
    private Pattern pattern = Pattern.compile(regexPat_no);
    private List<Token> queue = new ArrayList<>();
    private boolean hasMore;
    private LineNumberReader reader;
    private int lineNumber;

    public Lexer(Reader r) {
        hasMore = true;
        reader = new LineNumberReader(r);

    }

    /**
     * 读取token,读完即删除
     * @return token
     * @throws ParseException
     */
    public Token read() throws ParseException{
        if (fillQueue(0)) {
            return queue.remove(0);
        } else {
            return Token.EOF;
        }
    }

    /**
     * 读取read后几位token
     * @param i 索引
     * @return token
     * @throws ParseException
     */
    public Token peek(int i) throws ParseException{
        if (fillQueue(i)) {
            return queue.get(i);
        } else {
            return Token.EOF;
        }
    }
    /**
     * 在需要时解析输入的代码,
     * @param i 位置
     * @return 文档是否结束
     * @throws ParseException
     */
    private boolean fillQueue(int i) throws ParseException {
        while (i >= queue.size()) {
            if (hasMore) {
                readLine();
            } else {
                return false;
            }
        }
        return true;
    }

    /**
     * 将字符串转化
     * @param s 输入
     * @return
     */
    protected String toStringLiteral(String s) {
        StringBuffer sb = new StringBuffer();
        int len = s.length() - 1;
        //将两侧的 " 去掉
        for (int i = 1; i < len; i++) {
            char c = s.charAt(i);
            if (c == '\\' && i + 1 < len) {
                char c2 = s.charAt(i+1);
                if (c2 == '"' || c2 == '\\') {
                    c = s.charAt(++i);
                } else if (c2 == 'n'){
                    ++i;
                    c = '\n';
                }

            }
            sb.append(c);
        }
        return sb.toString();
    }

    /**
     * 读取每一行并添加单词
     * @throws ParseException
     */
    protected void readLine() throws ParseException {
        String line;
        try {
            line = reader.readLine();
            System.out.println("+++>" + line);
        } catch (IOException e) {
            throw new ParseException(e);
        }
        if (line == null) {
            hasMore = false;
            return;
        }
        int lineNumber = reader.getLineNumber();
        Matcher matcher = pattern.matcher(line);
        matcher.useTransparentBounds(true).useAnchoringBounds(false);
        int pos = 0;
        int endPos = line.length();
        while (pos < endPos) {
            matcher.region(pos,endPos);
            if (matcher.lookingAt()) {
//                System.out.println(line);
                addToken(lineNumber,matcher);
                pos = matcher.end();
            }else{
                throw new ParseException("bad token at line " + lineNumber);
            }
        }
        queue.add(new IdToken(lineNumber,Token.EOL));
    }

    /**
     * 通过正则来添加单词
     * @param lineNum 行数
     * @param matcher 匹配
     */
    private void addToken(int lineNum,Matcher matcher) {

        String m = matcher.group(1);    //
        if (m != null) {
//            System.out.println("g2:"+matcher.group(2));
            if (matcher.group(2) == null) { //注释
                Token t;
                if (matcher.group(3) != null) { //数字
                    t = new NumberToken(lineNum,Integer.parseInt(m));
                } else if (matcher.group(5) != null) {  //字符串
                    t = new StrToken(lineNum,toStringLiteral(m));
                }else{
                    t = new IdToken(lineNum,m);
                }
//                System.out.println("~~~~~~~: "+t.getString());
                queue.add(t);
            }

        }
    }

    /**
     * 标识符
     */
    protected static class IdToken extends Token{
        private String text;
        protected IdToken(int line,String id) {
            super(line);
            text = id;
        }

        @Override
        public boolean isIdentifier() {
            return true;
        }

        @Override
        public String getString() {
            return text;
        }
    }

    /**
     * 整形
     */
    protected static class NumberToken extends Token{
        private int value;
        protected NumberToken(int line,int value) {
            super(line);
            this.value = value;
        }

        @Override
        public boolean isNumber() {
            return true;
        }

        @Override
        public String getString() {
            return Integer.toString(value);
        }

        @Override
        public int getNumber() {
            return value;
        }
    }

    /**
     * 字符型
     */
    protected static class StrToken extends Token {
        String value;

        protected StrToken(int line, String value) {
            super(line);
            this.value = value;
        }

        @Override
        public String getString() {
            return value;
        }

        @Override
        public boolean isString() {
            return true;
        }
    }


}
